package com.cloudy.core.thrift.client;

import com.cloudy.core.common.ThriftProcessorManager;
import com.cloudy.core.thrift.zookeeper.client.IAddressProvider;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.apache.thrift.TServiceClient;
import org.apache.thrift.TServiceClientFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Set;

/**
 *  thrift客户端服务提供类
 *   包括多processor 具体使用参考test的client
 * Created by cloudy.xie on 16/11/18.
 */
public class ThriftClientSupport implements FactoryBean, InitializingBean {
    //////// 可以通过配置修改
    private Integer maxActive = 32;// 最大活跃连接数
    // 链接空闲时间 单位毫秒 default 3 min,
    private Integer idleTime = 180000;
    //通过配置获取
    private IAddressProvider addressProvider;
    ///////////end

    private Object proxyClient;
    private Class<?> objectClass;
    private GenericObjectPool<TServiceClient> pool;


    @Override
    public void afterPropertiesSet() throws Exception {
        Set<String> serviceNames = ThriftProcessorManager.getInstance().getProcessor().keySet();
        if(serviceNames == null || serviceNames .isEmpty()){
            return;
        }

        //classLoader
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        //poolConfig
        GenericObjectPool.Config poolConfig = initPoolConfig();

        serviceNames.stream().forEach(serviceName->{
            try {
                String faceName = ThriftProcessorManager.getInstance().getFaceName(serviceName);
                //clientPool
                ThriftClientPoolableObjectFactory clientPool = initClientPool(classLoader, faceName);
                //
                loadIfaceToPool(classLoader, faceName, poolConfig, clientPool);
            }catch (Exception e){
                //TODO
            }
        });

    }

    @Override
    public Object getObject() throws Exception {
        return proxyClient;
    }

    @Override
    public Class<?> getObjectType() {
        return objectClass;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    private GenericObjectPool.Config initPoolConfig() {
        GenericObjectPool.Config poolConfig = new GenericObjectPool.Config();
        poolConfig.maxActive = maxActive;
        poolConfig.minIdle = 0;
        poolConfig.minEvictableIdleTimeMillis = idleTime;
        poolConfig.timeBetweenEvictionRunsMillis = idleTime / 2L;
        return poolConfig;
    }

    private ThriftClientPoolableObjectFactory initClientPool(ClassLoader classLoader,String faceName) throws Exception {
        if(faceName == null){
            return null;
        }
        Class<TServiceClientFactory<TServiceClient>> fi = (Class<TServiceClientFactory<TServiceClient>>) classLoader
                .loadClass(faceName + "$Client$Factory");
        TServiceClientFactory<TServiceClient> clientFactory = fi.newInstance();
        return new ThriftClientPoolableObjectFactory(addressProvider, clientFactory);
    }

    /**
     * 加载Iface接口
     */
    private void loadIfaceToPool(ClassLoader classLoader,String faceName,
                                 GenericObjectPool.Config poolConfig,
                                 ThriftClientPoolableObjectFactory clientPool) throws ClassNotFoundException {
        if(faceName == null){
            return ;
        }
        objectClass = classLoader.loadClass(faceName + "$Iface");
        pool = new GenericObjectPool<TServiceClient>(clientPool, poolConfig);
        proxyClient = Proxy.newProxyInstance(classLoader, new Class[]{objectClass}, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //
                TServiceClient client = pool.borrowObject();
                try {
                    return method.invoke(client, args);
                } catch (Exception e) {
                    throw e;
                } finally {
                    pool.returnObject(client);
                }
            }
        });
    }

    public void setIdleTime(Integer idleTime) {
        this.idleTime = idleTime;
    }

    public void setMaxActive(Integer maxActive) {
        this.maxActive = maxActive;
    }

    public Integer getMaxActive() {
        return maxActive;
    }

    public Integer getIdleTime() {
        return idleTime;
    }

    public void setAddressProvider(IAddressProvider addressProvider) {
        this.addressProvider = addressProvider;
    }
}
